#ifndef AVALON_ASYNCHRONOUSCALLDETAIL_HPP
#define AVALON_ASYNCHRONOUSCALLDETAIL_HPP
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/type_traits.hpp>
#include "Avalon/Threading/Async.hpp"
#include "Avalon/Threading/Task.hpp"

namespace boost {
namespace detail {
  template<typename R, typename F, typename L>
  struct function_traits_helper<boost::_bi::bind_t<R, F, L >*> {
    BOOST_STATIC_CONSTANT(int, arity = 0);
    typedef R result_type;
  };

  template<typename T>
  struct function_traits_helper<boost::function<T>* > {
    BOOST_STATIC_CONSTANT(int, arity = 0);
    typedef typename boost::function<T>::result_type result_type;
  };
}
}

namespace Avalon {
namespace Threading {
namespace Detail {
  template<typename T>
  struct InvokeFunction {
    template<typename F, typename R>
    static void Invoke(F& function, R& result) {
      try {
        result->SetResult(function());
      } catch(boost::exception&) {
        result->SetException(boost::current_exception());
      } catch(std::exception& e) {
        result->SetException(e);
      }
    }
  };

  template<>
  struct InvokeFunction<void> {
    template<typename F, typename R>
    static void Invoke(F& function, R& result) {
      try {
        function();
        result->SetComplete();
      } catch(boost::exception&) {
        result->SetException(boost::current_exception());
      } catch(std::exception& e) {
        result->SetException(e);
      }
    }
  };

  template<typename F>
  class AsyncCallTask : public Task {
    public:
      typedef typename boost::function_traits<
        typename boost::remove_pointer<F>::type>::result_type ReturnType;

      AsyncCallTask(const typename AsyncOwner<ReturnType>::Ptr& result,
          const F& function)
          : Task(true),
            m_result(result),
            m_function(function) {}

      virtual ~AsyncCallTask() {}

    protected:
      virtual void ProcessTask() {
        IncreaseTaskCount();
        InvokeFunction<ReturnType>::Invoke(m_function, m_result);
        DecreaseTaskCount();
      }

    private:
      typename AsyncOwner<ReturnType>::Ptr m_result;
      F m_function;
  };

  template<typename T, typename F>
  AsyncCallTask<F>* CreateCallTask(const T& result, const F& function) {
    return new AsyncCallTask<F>(result, function);
  }
}
}
}

#endif // AVALON_ASYNCHRONOUSCALLDETAIL_HPP
